use askama::Template;
use axum::{
    extract::Query,
    response::{Html, Json},
    routing::get,
    Form, Router,
};
use noise::{NoiseFn, Perlin};
use serde::Deserialize;
use serde_json::{json, Value};

#[tokio::main]
async fn main() {
    let app = Router::new()
        .route("/", get(|| async { "hi" }))
        .route("/hi_json", get(hi_json))
        .route("/hi_html", get(hi_html))
        .route("/form", get(render_form).post(handle_form_submit))
        .route("/hello_template", get(hello_template))
        .route("/handle_mosaic_svg", get(handle_mosaic_svg));
    axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
        .serve(app.into_make_service())
        .await
        .unwrap();
}

async fn hi_json() -> Json<Value> {
    Json(json!({"domain":"localhost", "data":"hi"}))
}

async fn hi_html() -> Html<&'static str> {
    Html(
        "
        <h1>Hello HTML</h1>
        <p>Hello, World!</p>
    ",
    )
}

async fn render_form() -> Html<&'static str> {
    Html(
        r#"
        <html>
        <head>
            <title>Form Example</title>
        </head>
        <body>
            <h1>Form Example</h1>
            <form method="post">
                <label for="field1">Field 1:</label>
                <input type="text" name="field1" id="field1"><br>

                <label for="field2">Field 2:</label>
                <input type="text" name="field2" id="field2"><br>

                <input type="submit" value="Submit">
            </form>
        </body>
        </html>
    "#,
    )
}

#[derive(Deserialize)]
struct FormData {
    field1: String,
    field2: String,
}

async fn handle_form_submit(Form(formdata): Form<FormData>) -> Html<String> {
    let resp = format!(
        r#"
                <html>
        <head>
            <title>Form Submission Result</title>
        </head>
        <body>
            <h1>Form Submission Result</h1>
            <p>Field 1: {}</p>
            <p>Field 2: {}</p>
        </body>
        </html>
    "#,
        formdata.field1, formdata.field2
    );
    Html(resp)
}

#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate<'a> {
    name: &'a str,
}

async fn hello_template() -> HelloTemplate<'static> {
    HelloTemplate { name: "world" }
}

#[derive(Deserialize, Debug)]
struct MosaicSvgReq {
    x: u32,
    y: u32,
    z: u32,
}

#[derive(Template)]
#[template(path = "block.svg")]
struct MosaicSvgModel {
    width: u32,
    height: u32,
    x: u32,
    y: u32,
    z: u32,
    blocks: Vec<Vec<f64>>,
}

async fn handle_mosaic_svg(query: Query<MosaicSvgReq>) -> MosaicSvgModel {
    let max_length = 2f64.powi(5);
    let step_length = 1f64 / 2f64.powi(query.z as i32);
    let tile_length = 64;
    let perlin = Perlin::new(23123123);

    let mut xs: Vec<Vec<f64>> = Vec::with_capacity(16);
    for x in 0..tile_length {
        let mut ys: Vec<f64> = Vec::with_capacity(16);
        for y in 0..tile_length {
            let real_x =
                (query.x as f64 + (x as f64 / tile_length as f64)) * step_length * max_length;
            let real_y =
                (query.y as f64 + (y as f64 / tile_length as f64)) * step_length * max_length;
            let val: f64 = perlin.get([real_x, real_y]);
            let height: f64 = (val + 1.0) / 2f64;
            // println!("{}-{} {}", query.x, query.y, val);
            ys.push(height);
        }
        xs.push(ys);
    }

    let ht = MosaicSvgModel {
        width: tile_length,
        height: tile_length,
        x: query.x,
        y: query.y,
        z: query.z,
        blocks: xs,
    };
    ht
}
